package com.jokerpan.project.util;

import com.alibaba.fastjson.JSON;
import io.minio.*;
import io.minio.Result;
import io.minio.http.Method;
import io.minio.messages.Bucket;
import io.minio.messages.Item;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.TimeUnit;

@Component
@Slf4j
public class MinIOUtil {

    @Resource
    private MinioClient minioClient;

    /**
     * TODO 创建存储桶
     *
     * @param bucketName 存储桶名称
     * @return
     */
    public String makeBucket(String bucketName) throws Exception {
        //判断 存储桶存不存在
        boolean bucketExists = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
        if (bucketExists) {
            log.info("Minio 桶已经存在了:{}", bucketName);
        } else {
            //创建桶
            minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
            log.info("Minio 桶创建成功：{}", bucketName);
        }
        return bucketName;
    }

    /**
     * TODO 查询全部存储桶信息
     *
     * @return
     * @throws Exception
     */
    public List<Bucket> listBuckets() throws Exception {
        List<Bucket> listBuckets = minioClient.listBuckets();
        log.info("全部存储桶信息：{}", listBuckets);
        return listBuckets;
    }

    /**
     * TODO 查询单个储桶中的所有对象。
     *
     * @param bucketName 存储桶名称
     * @throws Exception
     */
    public List<Object> listObjects(String bucketName) throws Exception {
        Iterable<io.minio.Result<Item>> results = minioClient.listObjects(ListObjectsArgs.builder().bucket(bucketName).build());
        Iterator<Result<Item>> iterator = results.iterator();
        List<Object> items = new ArrayList<>();
        String format = "{'fileName':'%s','fileSize':'%s'}";
        while (iterator.hasNext()) {
            Item item = iterator.next().get();
            items.add(JSON.parse(String.format(format, item.objectName(),
                    formatFileSize(item.size()))));
        }
        log.info("查询单个储桶中的所有对象信息：{}", items);
        return items;
    }

    /**
     * TODO 删除一个存储桶
     *
     * @param bucketName 存储桶名称
     * @throws Exception
     */
    public void removeBucket(String bucketName) throws Exception {
        minioClient.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build());
        log.info("删除一个存储桶成功");
    }

    /**
     * TODO 获取对象的元数据
     * @param bucketName 存储桶名称。
     * @param objectName 存储桶里的对象名称
     * @return
     * @throws Exception
     */
    public StatObjectResponse statObject(String bucketName,String objectName) throws Exception {
        StatObjectResponse statObjectResponse = minioClient.statObject(StatObjectArgs.builder().bucket(bucketName).object(objectName).build());
        return statObjectResponse;
    }

    /**
     * TODO 上传多个文件
     * @param file 文件
     * @param bucketName 存储桶名称
     */
    public List<String>  putObject(MultipartFile[] file, String bucketName, String contents){
        List<String> path =new ArrayList<>();
        for (MultipartFile multipartFile : file) {
            //获取文件名
            String orgfileName = contents + "/" + multipartFile.getOriginalFilename();
            path.add(bucketName+"/"+orgfileName);
            try {
                //文件上传
                InputStream in = multipartFile.getInputStream();
                minioClient.putObject(
                        PutObjectArgs.builder().bucket(bucketName).object(orgfileName).stream(
                                in, multipartFile.getSize(), -1)
                                .contentType(multipartFile.getContentType())
                                .build());
                in.close();
            } catch (Exception e) {
                log.error(e.getMessage());
            }
        }
        return path;
    }

    /**
     * TODO 上传单个文件
     * @param file 文件对象
     * @param bucketName 存储桶名称
     * @param address 指定上传地址
     * @return 上传后地址
     */
    public String  putObject(MultipartFile file, String bucketName, String address){
        try {
            //文件上传
            InputStream in = file.getInputStream();
            minioClient.putObject(
                    PutObjectArgs.builder().bucket(bucketName).object(address).stream(
                            in, file.getSize(), -1)
                            .contentType(file.getContentType())
                            .build());
            in.close();
        } catch (Exception e) {
            log.error("MinIo文件上传失败" + e.getMessage());
            return null;
        }
        return address;
    }

    /**
     * TODO 将对象的数据下载到文件中
     * @param bucketName 存储桶名称
     * @param objectName 文件名称
     * @param fileName   文件名称
     * @throws Exception
     */
    public void downloadObject(String bucketName, String objectName, String fileName)throws Exception {
        log.info("bucketName = {}",bucketName);
        log.info("objectName = {}",objectName);
        log.info("fileName = {}",fileName);
        minioClient.downloadObject(DownloadObjectArgs.builder().bucket(bucketName).object(objectName).filename(fileName).build());
    }


    /**
     * TODO 删除文件
     * @param bucketName 存储桶名称
     * @param objectName 文件名称
     * @throws Exception
     */
    public void  removeObject(String bucketName, String objectName)throws Exception {
        minioClient.removeObject(RemoveObjectArgs.builder().bucket(bucketName).object(objectName).build());
    }

    /**
     * TODO 生成一个给HTTP GET请求用的presigned URL。
     *      浏览器/移动端的客户端可以用这个URL进行下载，
     *      即使其所在的存储桶是私有的。
     *      这个presigned URL可以设置一个失效时间，默认值是7天
     *
     * @param bucketName 存储桶名称
     * @param objectName 文件名称
     * @param expiry     失效时间(单位小时)
     * @return
     */
    public String getPresignedObjectUrl(String bucketName, String objectName, Integer expiry)throws Exception {
        log.info("bucketName = {}",bucketName);
        log.info("objectName = {}",objectName);
        log.info("expiry = {}",expiry);
        String presignedObjectUrl = minioClient.getPresignedObjectUrl(
                GetPresignedObjectUrlArgs
                        .builder()
                        .method(Method.GET)
                        .bucket(bucketName)
                        .object(objectName)
                        .expiry(expiry, TimeUnit.HOURS)
                        .build());
        log.info("生成http 下载信息：{}",presignedObjectUrl);
        return presignedObjectUrl;
    }

    /**
     * 判断文件是否存在
     *
     * @param bucketName 存储桶名称
     * @param objectName 文件名称, 如果要带文件夹请用 / 分割, 例如 /help/index.html
     * @return true存在, 反之
     */
    public Boolean checkFileIsExist(String bucketName ,String objectName) {
        try {
            minioClient.statObject(
                    StatObjectArgs.builder().bucket(bucketName).object(objectName).build()
            );
        } catch (Exception e) {
            return false;
        }
        return true;
    }

    /**
     * 上传 bytes 通用方法
     *  @param bucketName 桶名称
     *  @param objectName
     *  @param bytes      字节编码
     */
    public void putBytes(String bucketName, byte[] bytes, String objectName, String contentType) {
        try {
            // 字节转文件流
            InputStream stream = new ByteArrayInputStream(bytes);
            minioClient.putObject(
                    PutObjectArgs.builder()
                            .bucket(bucketName)
                            .object(objectName)
                            .contentType(contentType)
                            .stream(stream, stream.available(), -1)
                            .build()
            );
            stream.close();
        } catch (Exception e) {
            log.error("minio文件流上传错误" + e.getMessage(), e);
            throw new RuntimeException("文件流上传错误", e);
        }
    }

    /**
     * 复制文件到指定路径
     * @param bucketName 存储桶名称
     * @param source 源文件地址
     * @param target 目标地址
     * @return
     */
    public void copyObject(String bucketName,String source, String target) {
        try {
            minioClient.copyObject(
                    CopyObjectArgs.builder()
                            .bucket(bucketName)
                            .object(target)
                            .source(
                                    CopySource.builder()
                                            .bucket(bucketName)
                                            .object(source)
                                            .build())
                            .build());
        }catch (Exception e) {
            log.error("minio文件复制失败" + e.getMessage(), e);
            throw new RuntimeException("文件复制失败", e);
        }
    }


    public static String formatFileSize(long fileS) {
        DecimalFormat df = new DecimalFormat("#.00");
        String fileSizeString = "";
        String wrongSize = "0B";
        if (fileS == 0) {
            return wrongSize;
        }
        if (fileS < 1024) {
            fileSizeString = df.format((double) fileS) + " B";
        } else if (fileS < 1048576) {
            fileSizeString = df.format((double) fileS / 1024) + " KB";
        } else if (fileS < 1073741824) {
            fileSizeString = df.format((double) fileS / 1048576) + " MB";
        } else {
            fileSizeString = df.format((double) fileS / 1073741824) + " GB";
        }
        return fileSizeString;
    }
}
